<?php
/**
 * geo_users.php
 *
 * INPUT: 	$_REQUEST['user']
 * 			$_REQUEST['lat']
 * 			$_REQUEST['lon']
 * OUTPUT: 	an XML file with related users and their distances to given user.
 *
 * Speaks for its own I hope.
 * 
 * By Laurens Van Keer
 */
require 'auth.php';

define('MAX_AGE', 30); // db entry is max. 30 minutes old
define('MAX_DISTANCE', 10); // user is max. 10 kms away
define('MIN_SCORE', 0.3); // users relate when tasteometer score is at least 30%

// CONNECT TO DB
require('config.php');
$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('<Users>' . PHP_EOL . '<Error>Error connecting to mysql!</Error>' . PHP_EOL . '</Users>');
mysql_select_db($dbname);
// DONE CONNECTING

header('Content-Type: text/xml');
echo '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' . PHP_EOL;
echo '<Users>' . PHP_EOL;

$apiClass = new lastfmApi();
$tasteClass = $apiClass->getPackage($auth, 'tasteometer', $config);

if ( empty($_REQUEST['user']) ) {
	die('<Error>No user specified!</Error>' . PHP_EOL . '</Users>');
} else {

	// get settings if necessary
	if ( empty($_REQUEST['max_age']) )
		$max_age = MAX_AGE;
	else
		$max_age = (float) $_REQUEST['max_age'];
		
	if ( empty($_REQUEST['max_distance']) )
		$max_distance = MAX_DISTANCE;
	else
		$max_distance = (float) $_REQUEST['max_distance'];
		
	if ( empty($_REQUEST['min_score']) )
		$min_score = MIN_SCORE;
	else
		$min_score = (float) $_REQUEST['min_score'];

	// get input username
	$user = mysql_real_escape_string(htmlentities($_REQUEST['user']));
	$input = array('type' => "user", 'value' => $user);

	// get lat/lon coordinates
	$lat = mysql_real_escape_string($_REQUEST['lat']);
	$lon = mysql_real_escape_string($_REQUEST['lon']);

	// max. age of db entry en thus min. timestamp
	$curTime = time();
	$minTime = $curTime - $max_age * 60;

	// check for abuse first
	$ip = $_SERVER['REMOTE_ADDR'];
	$query = "SELECT user FROM geo_users WHERE lastip='$ip' AND time>$minTime";
	$qresult = @mysql_query($query);
	// more than 10 results? Something's not right!
	if (@mysql_num_rows($qresult) > 10)
		die('<Error>Please don\'t abuse our server! :( ' . 
			'The coordinates of at least 10 Last.fm users have been updated from your IP-address in the past ' . 
			$max_age . ' minutes!!!</Error>' . PHP_EOL . '</Users>');

	// does this user already exist in our db?
	$query = "SELECT user FROM geo_users WHERE user='$user'";
	$qresult = @mysql_query($query);
	if (@mysql_num_rows($qresult) == 0) {

		// insert user
		$query = "INSERT INTO geo_users (user, lastip, time, latitude, longitude) VALUES ('$user', '" . $_SERVER['REMOTE_ADDR'] . "', '$curTime', '$lat', '$lon')";

		if ($qresult = @mysql_query($query)) {
			@mysql_free_result($qresult);
		} else {
			die('<Error>Cannot insert user into database!</Error>' . PHP_EOL . '</Users>');
		}

	} else {

		// update time & coordinates
		$query = "UPDATE geo_users SET lastip='$ip', time='$curTime', latitude='$lat', longitude='$lon' WHERE user='$user'";

		if ($qresult = @mysql_query($query)) {
			@mysql_free_result($qresult);
		} else {
			die('<Error>Cannot update user entry in database!</Error>' . PHP_EOL . '</Users>');
		}

	}

	// query the db for all users (except given one)
	$query = "SELECT * FROM geo_users WHERE user != '$user' AND time >= $minTime";

	if (($qresult = @mysql_query($query)) == false) {
		die('<Error>' . htmlentities(mysql_error()) . '</Error>' . PHP_EOL . '</Users>');
	}

	// loop through results and compare taste
	$id = 0;
	while ($row = mysql_fetch_assoc($qresult)) {
		$comparison = array('type' => "user", 'value' => htmlentities($row['user']));
		$methodVars = array(1 => $input, 2 => $comparison);
		//echo "<array>".print_r($methodVars)."</array>";
		
		$result = $tasteClass->compare($methodVars);

		// check if users can relate
		$distance = distance($lat, $lon, $row['latitude'], $row['longitude']);
		
		if ($result['score'] > $min_score && $distance <= $max_distance) {
			$age = round(($curTime - (int)$row['time']) / 60); // age of db entry in minutes
			$score = round($result['score'] * 100); // tasteometer %		

			echo '<User>' . PHP_EOL;
				echo '<Id>' . $id++ . '</Id>' . PHP_EOL;
				echo '<Name>' . $row['user'] . '</Name>' . PHP_EOL;
				echo "<Age>$age</Age>" . PHP_EOL;
				echo '<Url>' . htmlentities($result['inputTwo']['url']) . '</Url>' . PHP_EOL;
				echo "<Score>$score</Score>" . PHP_EOL;
				echo '<Latitude>' . $row['latitude'] . '</Latitude>' . PHP_EOL;
				echo '<Longitude>' . $row['longitude'] . '</Longitude>' . PHP_EOL;
				echo "<Distance>$distance</Distance>" . PHP_EOL;
				echo '<Thumbnail_small>' . htmlentities($result['inputTwo']['image']['small']) . '</Thumbnail_small>' . PHP_EOL;
				echo '<Thumbnail_medium>' . htmlentities($result['inputTwo']['image']['medium']) . '</Thumbnail_medium>' . PHP_EOL;
				echo '<Thumbnail_large>' . htmlentities($result['inputTwo']['image']['large']) . '</Thumbnail_large>' . PHP_EOL;
			echo '</User>' . PHP_EOL;		

		}	

	}	

}

echo '</Users>' . PHP_EOL;

/**
 * Calculates distance in km, given 2 geographical coordinates (lat/lon in decimal degrees).
 */
function distance($lat1, $lon1, $lat2, $lon2)
{
	$theta = $lon1 - $lon2;
	$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
	$dist = acos($dist);
	$dist = rad2deg($dist);
	$result = round($dist * 60 * 1.1515 * 1.609344, 3); // in km
	return isNan($result)?0:$result;
}

function isNaN($var)
{
	return !ereg ("^[-]?[0-9]+([\.][0-9]+)?$", $var);
}
?>